<?php

namespace extend\bridgecli;

use extend\bridgecli\client\HttpClient;
use extend\bridgecli\client\Socks5Client;

class BridgeProxy
{
    /**
     * @var HttpClient[]
     */
    public $httpProxyClients = [];
    /**
     * @var Socks5Client[]
     */
    public $socks5ProxyClients = [];

    public function __construct()
    {
        \Swoole\Timer::tick(300000, function (){
            $time = time();
            foreach ($this->httpProxyClients as $httpClient){
                if($httpClient->lastTime + 600 < $time){
                    $httpClient->close(null, 0);
                    unset($this->httpProxyClients[$httpClient->addr['tofd']]);
                }
            }
        });

        \Swoole\Timer::tick(300000, function (){
            $time = time();
            foreach ($this->socks5ProxyClients as $socks5Client){
                if($socks5Client->lastTime + 600 < $time){
                    $socks5Client->close(null, 0);
                    unset($this->socks5ProxyClients[$socks5Client->addr['tofd']]);
                }
            }
        });
    }

    /**
     * http连接消息
     * @param \Server $server
     * @param int $tofd
     * @param string $data
     * @return void
     */
    public function httpConnect(mixed $server, int $tofd, array $data)
    {
        $httpClient = new HttpClient($this);
        $data['body'] = base64_decode($data['body']);
        if($httpClient->connect($server, $tofd, $data)){
            $this->httpProxyClients[$tofd] = $httpClient;
        }
    }
    /**
     * http通信消息
     * @param \Server $server
     * @param int $tofd
     * @param string $data
     * @return void
     */
    public function httpMessage(mixed $server, int $tofd, string $data)
    {
        $httpClient = $this->httpProxyClients[$tofd] ?? null;
        if($httpClient && $data){
            $httpClient->send($server, $tofd, $data);
        }
    }
    /**
     * http关闭消息
     * @param \Server $server
     * @param int $tofd
     * @param string $data
     * @return void
     */
    public function httpClose(mixed $server, int $tofd)
    {
        $httpClient = $this->httpProxyClients[$tofd] ?? null;
        if($httpClient){
            $httpClient->close($server, $tofd);
            unset($this->httpProxyClients[$tofd]);
        }
    }
    /**
     * socks5连接消息
     * @param \Server $server
     * @param int $tofd
     * @param string $data
     * @return void
     */
    public function socks5Connect(mixed $server, int $tofd, array $data)
    {
        $socks5Client = new Socks5Client($this);
        if($socks5Client->connect($server, $tofd, $data)){
            $this->socks5ProxyClients[$tofd] = $socks5Client;
        }
    }
    /**
     * socks5消息
     * @param \Server $server
     * @param int $tofd
     * @param string $data
     * @return void
     */
    public function socks5Message(mixed $server, int $tofd, string $data)
    {
        $socks5Client = $this->socks5ProxyClients[$tofd] ?? null;
        if($socks5Client && $data){
            $socks5Client->send($server, $tofd, $data);
        }
    }
    /**
     * socks5关闭消息
     * @param \Server $server
     * @param int $tofd
     * @param string $data
     * @return void
     */
    public function socks5Close(mixed $server, int $tofd)
    {
        $socks5Client = $this->socks5ProxyClients[$tofd] ?? null;
        if($socks5Client){
            $socks5Client->close($server, $tofd);
            unset($this->socks5ProxyClients[$tofd]);
        }
    }

    /**
     * 桥接dns解析
     * @param string $domainName
     * @return array
     */
    public function gethostbyname(string $domainName)
    {
        $domainName = str_replace(['[', ']'], '', $domainName);
        if(!filter_var($domainName, FILTER_VALIDATE_IP)){
            $addr = \Swoole\Coroutine\System::gethostbyname($domainName,AF_INET, 5);
            if($addr){
                return [$addr, SOCK_STREAM, AF_INET];
            }else{
                $addr = \Swoole\Coroutine\System::gethostbyname($domainName,AF_INET6, 5);
                if($addr) return [$addr, SOCK_STREAM, AF_INET6];
            }
            return [$domainName, SOCK_STREAM, AF_INET];
        }else{
            $domain = AF_INET;
            if (filter_var($domainName, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                $domain = AF_INET6;
            }
            return [$domainName, SOCK_STREAM, $domain];
        }
    }
}